package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.service.*;
import com.ruoyi.business.component.RepaymentMqSendService;
import com.ruoyi.business.domain.TransferVo;
import com.ruoyi.business.mapper.BRepaymentMapper;
import com.ruoyi.business.service.*;
import com.ruoyi.business.task.thread.TransactionThread;
import com.ruoyi.business.task.thread.TransferToUsThread;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.business.util.RepaymentUtils;
import com.ruoyi.common.constant.LoanConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.FeishuUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.extdata.common.RetryFunction;
import com.ruoyi.extdata.common.config.MonnifyConfig;
import com.ruoyi.extdata.service.IExternalApiService;
import com.ruoyi.sms.service.ISmsService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDictDataService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BRepaymentServiceImpl extends BaseService implements IBRepaymentService {

    @Autowired
    BRepaymentMapper repaymentMapper;

    @Autowired
    ICreditAccountService creditAccountService;

    @Autowired
    ISmsService smsService;

    @Autowired
    IRepaymentOrderService repaymentOrderService;

    @Autowired
    IBLoanInfoService loanInfoService;

    @Autowired
    ICustomerInfoService customerInfoService;

    @Autowired
    ILoanOffsetService loanOffsetService;

    @Autowired
    ICustBankCardService custBankCardService;

    @Autowired
    RedisCache redisCache;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    IExternalApiService iExternalApiService;

    @Autowired
    ISysConfigService iSysConfigService;

    @Autowired
    IExtMonnifyOrderService iExtMonnifyOrderService;

    @Autowired
    IRepaymentOrderService iRepaymentOrderService;

    @Autowired
    IRepaymentService iRepaymentService;
    @Autowired
    IDoRepaymentLastService doRepaymentLastService;

    @Autowired
    IRealNameAuthService iRealNameAuthService;

    @Autowired
    IExtMonnifyReservedAccountsService iExtMonnifyReservedAccountsService;

    @Autowired
    ILoanIouService iLoanIouService;

    @Autowired
    IReduceFeeService iReduceFeeService;

    @Autowired
    MonnifyConfig monnifyConfig;

    @Autowired
    IRepaymentTransferService iRepaymentTransferService;

    @Autowired
    IBusinessService iBusinessService;

    @Autowired
    ILoanOverflowService iLoanOverflowService;

    @Autowired
    IBLoanInfoService iBLoanInfoService;

    @Autowired
    @Lazy
    IRepaymentSmsService iRepaymentSmsService;

    @Lazy
    @Autowired
    IMonnifyRepaymentService monnifyRepaymentService;

    @Autowired
    IRepaymentExceptionService iRepaymentExceptionService;

    @Autowired
    RetryFunction retryFunction;

    @Autowired
    private ISysDictDataService dataService;

    @Autowired
    IRepaymentService repaymentService;

    @Autowired
    private RepaymentMqSendService repaymentMqService;

    @Autowired
    IRepayToWalletService repayToWalletService;

    @Override
    public List<CreditAccount> getBatchRepaymentAccount(int startLimit, int step) {
        return repaymentMapper.getBatchRepaymentAccount(startLimit, step);
    }

    @Override
    public List<CreditAccount> getBatchRepaymentAccount() {
        return repaymentMapper.getBatchRepaymentAccountAll();
    }

    @Override
    public int getBatchRepaymentCustCount() {
        Integer result = repaymentMapper.getBatchRepaymentCustCount();
        if (null == result) {
            result = 0;
        }
        return result;
    }

    @Override
    public void doBatchRepaymentByAccount(CreditAccount account, int type) {
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + account.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();
            //执行账户跑批
            this.doAccountBatchRepayment(account, type);
            //无需重试
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("doBatchRepayment线程sleep异常", e);
        } finally {
           lock.unlock();
        }
    }

    private RepaymentOrder splitRepaymentOrder(RepaymentOrder src, String bankCard, BigDecimal repaymentAmt, BigDecimal withholdRate, String bathNo) {
        RepaymentOrder r = new RepaymentOrder();
        BeanUtils.copyBeanProp(r, src);
        r.setId(null);
        r.setRepaymentNo(IdUtils.reqId());
        r.setRepaymentAmt(repaymentAmt.setScale(0, BigDecimal.ROUND_UP));
        r.setBankCard(bankCard);
        r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        r.setPayTransSerialNo(null);
        Date now = new Date();
        r.setCreateTime(now);
        r.setUpdateTime(now);
        r.setWithholdRate(withholdRate);
        r.setBathNo(bathNo);
        repaymentOrderService.insertRepaymentOrder(r);
        return r;
    }

    //执行账户批量代扣
    //type 1 上午   2下午
    private void doAccountBatchRepayment(CreditAccount account, int type) {
        Date batchDate = new Date();
        //查询该用户借据，并生成repaymentOrder
        Integer minDdays = redisCache.getCacheObject("cardBatchSplitDebit:" + type + ":minDdays");
        Integer maxDays = redisCache.getCacheObject("cardBatchSplitDebit:" + type + ":maxDays");
        // 早上代扣规则减去天数
        Integer subtractDays = redisCache.getCacheObject("cardBatchSplitDebit:" + type + ":subtractDays");
        Integer surplusDays = redisCache.getCacheObject("cardBatchSplitDebit:" + type + ":surplusDays");
        List<LoanIou> loanIouList = iBLoanInfoService.getBatchJobActiveLoanIouAll(account.getCustNo(), minDdays, maxDays, subtractDays, surplusDays);

        for (LoanIou loan : loanIouList) {
            log.info(loan.getLoanSerialNo()+"执行代扣操作:{}",loan.toString());
            try {
                //还款日
                Date repayDate = DateUtils.parseDate(loan.getRepayDate(), DateUtils.YYYY_MM_DD_SLASH);
                log.info("批量代扣：是否延期："+loan.getIsDeferred());
                if (null != loan.getIsDeferred() && loan.getIsDeferred() == 1) {
                    // 延期的还款日
                    repayDate = DateUtils.parseDate(loan.getDeferredRepayDate(), DateUtils.YYYY_MM_DD_SLASH);
                    log.info("批量代扣：是否延期：日期{}",repayDate);
                }

                if (!batchDate.before(repayDate)) {

                    //检查金额是否低于阀值，有无溢缴款，并判断溢缴款是否小于阀值
                    if (!checkAmount(loan, "DateBatch")) {
                        log.info("检查金额（溢缴款）时已完成,LoanSerialNo:{}", loan.getLoanSerialNo());
                        continue;
                    }

                    //判断借据还款日是否等于当天-发起代扣
                    //发起代扣申请记录
                    RepaymentOrder repaymentOrder;
                    if (type == 1) {
                        repaymentOrder = iBLoanInfoService.createRepaymentOrder(loan, null, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, BigDecimal.ONE, "AM");
                    } else {
                        repaymentOrder = iBLoanInfoService.createRepaymentOrder(loan, null, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, BigDecimal.ONE, "PM");
                    }
                    if (repaymentOrder != null) {
                        this.doBatchRepayment(loan, repaymentOrder);
                    }
                }
            } catch (Exception e) {
                log.error("生成repaymentOrder错误:{}>>>>>{}", loan.getCustNo(), e);
                continue;
            }
        }
    }

    public boolean checkAmountLock(LoanIou iou, String reduceSource) {
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + iou.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        long start = System.currentTimeMillis();
        try {
            //执行账户跑批
            return checkAmount(iou, reduceSource);
        } catch (Exception e){
            log.error("checkAmountLock", e);
            String className = this.getClass().getName();
            String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
            FeishuUtils.sendFeishuToMonit(className+"."+methodName,"checkAmountLock",iou.getCustNo(),iou.getLoanSerialNo(),e);
        } finally {
            //释放锁
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
            lock.unlock();
        }
        return true;
    }

    /**
     * 检查金额是否低于阀值，有无溢缴款，并判断溢缴款是否小于阀值
     *
     * @param iou
     * @return 该借据是否还需要冲账
     */
    public boolean checkAmount(LoanIou iou, String reduceSource) {
        log.info("是否小于50n::{}",iou.getLoanSerialNo());
        if (iou.getIouStatus() == LoanIou.LOAN_IOU_STATUS_SETTLE) {
            return false;
        }

//        BigDecimal needTotal = iou.getLoanPrincipal().add(iou.getLoanInterest()).add(iou.getServiceFee()).add(iou.getLateFee());
//        BigDecimal repaidTotal = iou.getRepaidPrincipal().add(iou.getRepaidInterest()).add(iou.getRepaidServiceFee()).add(iou.getRepaidLateFee());
        BigDecimal repaymentAmt = LoanIou.calculationOverdueAmount(iou);
        String repaymentReduceAmount = iSysConfigService.selectConfigByKey("repayment_reduce_amount");
        //欠款金额小于减免阀值  直接减免
        if (StringUtils.isNotBlank(repaymentReduceAmount) && repaymentAmt.doubleValue() < Double.parseDouble(repaymentReduceAmount)) {

            log.info("是否小于50n::{},小于50直接减免",iou.getLoanSerialNo());
            BigDecimal lateFee = iou.getLateFee().subtract(iou.getRepaidLateFee()).subtract(iou.getFeeRemissionAmt());
            BigDecimal principal = iou.getLoanPrincipal().subtract(iou.getRepaidPrincipal());
            log.info("检查金额是否低于阀值，lateFee:{}，principal:{}",lateFee,principal);
            if (lateFee.doubleValue() > 0) {
                log.info("检查金额是否低于阀值，存在罚息不能处理:{}",iou.getLoanSerialNo());
                return true;
               // systemReduceRepayemnt(iou, 1, lateFee, reduceSource);
            }
            if (principal.doubleValue() > 0) {
                systemReduceRepayemnt(iou, 2, principal, reduceSource);
            }


            //再查询对应的loanIou是否需要还款
            iou = iLoanIouService.selectLoanIouById(iou.getId());
            if (iou.getIouStatus() == LoanIou.LOAN_IOU_STATUS_SETTLE) {
                return false;
            }
        }

        return true;
    }

    /**
     * 系统减免逻辑
     *
     * @param iou
     * @param type   1滞纳金  2本金
     * @param amount
     */
    public void systemReduceRepayemnt(LoanIou iou, int type, BigDecimal amount, String reduceSource) {
        log.info("系统减免逻辑:type:{},amount:{},reduceSource:{},iou:{}",type,amount,reduceSource,iou.toString());
        if (type != 1 && type != 2) {
            log.info("系统减免逻辑接受type异常{},LoanSerialNo:", type, iou.getLoanSerialNo());
            return;
        }

        //从逻辑上来说   只存在滞纳金减免和本金减免
        ReduceFee reduceFee = new ReduceFee();
        reduceFee.setReduceSerialNo(IdUtils.reqId());
        reduceFee.setLoanSerialNo(iou.getLoanSerialNo());
        reduceFee.setCustNo(iou.getCustNo());
        reduceFee.setTotalAmt(amount);
        if (type == 1) {
            reduceFee.setLateFee(amount);
        } else {
            reduceFee.setLoanPrincipal(amount);
        }

        reduceFee.setReduceSource(reduceSource);
        reduceFee.setReduceDesc("");
        iReduceFeeService.insertReduceFee(reduceFee);

        //冲账
        iRepaymentService.repaymentNoLock(iou, reduceFee.getReduceSerialNo(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_SYSTEM_REDUCE, amount, BigDecimal.ZERO, 2);

    }

    //@Override
//    public void deferredWithholdingLock(RepaymentOrder repaymentOrder, Boolean isSuccess) {
//        //锁住客户账户再操作
//        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + repaymentOrder.getCustNo();
//
//        int j = 0;//重试次数
//        while (j < LoanConstants.CUST_ACCOUNT_LOCK_REGET_COUNT) {//重试次数内
//            j++;
//            log.info("deferredWithholding客户号{}第{}次获取账户锁", repaymentOrder.getCustNo(), j);
//            String lockValue = IdUtils.fastSimpleUUID();
//            boolean lock = redisCache.lock(lockKey, lockValue);//获取锁
//
//            if (lock) {
//                long start = System.currentTimeMillis();
//                try {
//
//                    //执行账户跑批
//                    if (isSuccess) {
//                        this.deferredWithholdingSuccess(repaymentOrder);
//                        break;
//                    } else {
//                        this.deferredWithholdingFail(repaymentOrder);
//                        break;
//                    }
//
//                } finally {
//                    //释放锁
//                    long end = System.currentTimeMillis();
//                    log.info("锁占用时间，耗时：" + (end - start) + " ms");
//                    redisCache.unLock(lockKey, lockValue);
//                }
//            } else {
//                log.info("deferredWithholding客户号{}第{}次获取账户锁失败", repaymentOrder.getCustNo(), j);
//
//                try {
//                    Thread.sleep(LoanConstants.CUST_ACCOUNT_LOCK_SLEEP_TIME);
//                } catch (InterruptedException e) {
//                    log.error("deferredWithholding线程sleep异常", e);
//                }
//            }
//        }
//    }

    //延时代扣逻辑
    @Override
    public void deferredWithholdingFail(RepaymentOrder repaymentOrder) {
        LoanIou loanIou = new LoanIou();
        loanIou.setLoanSerialNo(repaymentOrder.getLoanSerialNo());
        List<LoanIou> loanIous = iLoanIouService.selectLoanIouList(loanIou);

        if (!BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH.equals(repaymentOrder.getActionType())
                && null == repaymentOrder.getLoanDeferOrderNo() ) {
            // 发送短信
               iRepaymentSmsService.activeRepayment(repaymentOrder,loanIous.get(0), "fail");
            log.info("类型不为DateBatch,直接出去，RepaymentNo:{}", repaymentOrder.getRepaymentNo());
            return;
        }

        //根据借据去冲账  判断借据是否是已完成状态

        boolean today;
        try {
            today = DateUtils.isToday(repaymentOrder.getCreateTime());
        } catch (Exception e) {
            log.error("CreateTime格式转换失败,RepaymentNo：{}", repaymentOrder.getRepaymentNo(), e);
            return;
        }
        if (loanIous.size() != 0 && loanIous.get(0).getIouStatus() != LoanIou.LOAN_IOU_STATUS_SETTLE && today) {
            //借据未完成，进入换卡代扣
            List<CustBankCard> bankList;
            Map<String, Object> stringObjectMap = doBatchRepaymentBankList(repaymentOrder);
            bankList = (List<CustBankCard>) stringObjectMap.get("bankList");

            RepaymentOrder newRepaymentOrder = loanInfoService.createRepaymentOrder(loanIous.get(0), bankList.get(0).getAuthorizationCode(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, null, BigDecimal.ONE, repaymentOrder.getBathNo());
            log.info("换卡代扣，RepaymentNo:" + repaymentOrder.getRepaymentNo());
            this.doBatchRepayment(loanIous.get(0), newRepaymentOrder, bankList, bankList.get(0).getEmail());

        }
    }

    @Override
    @Async("threadPoolTaskExecutor")
    public void saveRepaymentException(String relationNo, String type, BigDecimal repaymentAmt, String custNo) {

        RepaymentException repaymentException = new RepaymentException();
        repaymentException.setRelationNo(relationNo);
        repaymentException.setType(type);
        repaymentException.setAmount(repaymentAmt);
        repaymentException.setCustNo(custNo);
        iRepaymentExceptionService.insertRepaymentException(repaymentException);
    }

    //延时代扣逻辑
    @Override
    @Transactional
    public void deferredWithholdingSuccess(RepaymentOrder repaymentOrder) {
        //根据借据去冲账  判断借据是否是已完成状态
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
        repaymentOrderService.updateById(repaymentOrder);
        LoanIou loanIou = iLoanIouService.getLoanIouByLoanSerialNo(repaymentOrder.getLoanSerialNo());
        if (null!=loanIou && LoanIou.LOAN_IOU_STATUS_SETTLE != loanIou.getIouStatus()) {
            //借据未完成，进入冲账逻辑
//            repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
//            repaymentOrder.setPayTime(new Date());
//            iRepaymentOrderService.updateRepaymentOrder(repaymentOrder);
            // 判断是否有减免，有先进行减免冲账
            if(null!=repaymentOrder.getCustReduceOrderId()){
                repaymentService.doReduceAmtStrike(repaymentOrder);
            }
            iRepaymentService.repaymentNoLock(loanIou, repaymentOrder.getRepaymentNo(), repaymentOrder.getActionType(), repaymentOrder.getRepaymentAmt(), BigDecimal.ZERO, 1);
            goDateBatchWithhold(repaymentOrder);
            doRepaymentLastService.lastInitRuleInAndAutoMat(repaymentOrder);
        } else {
            // 借据都结清了，再过来的数据进入溢缴款
            log.info("借据都结清了，再过来的数据进入溢缴款");
            iBLoanInfoService.doOverFlowAndWallet(repaymentOrder.getRepaymentAmt(),repaymentOrder);
           // dealUnusualIou(repaymentOrder, loanIou);
        }
    }


    /**
     * 主动还款处理
     * @param repaymentOrder
     */
    @Override
    public void qucikRepayWithhol(RepaymentOrder repaymentOrder) {
        //根据借据去冲账  判断借据是否是已完成状态
        LoanIou loanIou = iLoanIouService.getLoanIouByLoanSerialNo(repaymentOrder.getLoanSerialNo());
        if (null!=loanIou && LoanIou.LOAN_IOU_STATUS_SETTLE != loanIou.getIouStatus()) {
            //借据未完成，进入冲账逻辑
//            repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
//            repaymentOrder.setPayTime(new Date());
//            iRepaymentOrderService.updateRepaymentOrder(repaymentOrder);
            // 判断是否有减免，有先进行减免冲账
            if(null!=repaymentOrder.getCustReduceOrderId()){
                repaymentService.doReduceAmtStrike(repaymentOrder);
            }
            iRepaymentService.repaymentNoLock(loanIou, repaymentOrder.getRepaymentNo(), repaymentOrder.getActionType(), repaymentOrder.getRepaymentAmt(), BigDecimal.ZERO, 1);
            //发送短信-通知告诉用户还款成功
            //事物处理提出
            // iRepaymentSmsService.activeRepayment(repaymentOrder, "success");
        } else {
            iBLoanInfoService.doOverFlowAndWallet(repaymentOrder.getRepaymentAmt(), repaymentOrder);
            //dealUnusualIou(repaymentOrder, loanIou);
        }
    }


    /**
     * DelayDateBatch
     * @param repaymentOrder
     */
    private void goDateBatchWithhold(RepaymentOrder repaymentOrder) {
        LoanIou loanIou = iLoanIouService.getLoanIouByLoanSerialNo(repaymentOrder.getLoanSerialNo());
        boolean today;
        try {
            today = DateUtils.isToday(repaymentOrder.getCreateTime());
        } catch (Exception e) {
            log.error("CreateTime格式转换失败,RepaymentNo：{}", repaymentOrder.getRepaymentNo(), e);
            return;
        }
        //冲账完后重新查询该借据，如果借据状态不能为结清，则继续执行代扣逻辑
        //并且查看回调的repaymentOrder是不是属于DateBatch类型
        //并且查看回调是否是今天的，不是今天的则不进入二次代扣逻辑
        //并且已检查溢缴款及减免额度
        if (null != loanIou
                && BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH.equals(repaymentOrder.getActionType())
                && today
                && !checkAmount(loanIou, "DelayDateBatch")) {

            BigDecimal withholdRate = getwithholdRate(repaymentOrder);

            //本次还款金额
            BigDecimal needTotal = loanIou.getLoanPrincipal().add(loanIou.getLoanInterest()).add(loanIou.getServiceFee()).add(loanIou.getLateFee());
            BigDecimal repaidTotal = loanIou.getRepaidPrincipal().add(loanIou.getRepaidInterest()).add(loanIou.getRepaidServiceFee()).add(loanIou.getRepaidLateFee());
            BigDecimal repaymentAmt = needTotal.subtract(repaidTotal);
            BigDecimal newAmount = repaymentAmt.multiply(withholdRate).setScale(4, BigDecimal.ROUND_UP);

            RepaymentOrder newRepaymentOrder;
            if (BigDecimal.ZERO.doubleValue() == withholdRate.doubleValue()) {
                // 获取银行卡列表
                Map<String, Object> stringObjectMap = doBatchRepaymentBankList(repaymentOrder);
                List<CustBankCard> bankList = (List<CustBankCard>) stringObjectMap.get("bankList");

                newRepaymentOrder = loanInfoService.createRepaymentOrder(loanIou, bankList.get(0).getAuthorizationCode(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, newAmount, withholdRate, repaymentOrder.getBathNo());
                log.info("换卡代扣，RepaymentNo:" + repaymentOrder.getRepaymentNo());
                this.doBatchRepayment(loanIou, newRepaymentOrder, bankList, bankList.get(0).getEmail());
            } else {
                //生成repaymentOrder
                newRepaymentOrder = loanInfoService.createRepaymentOrder(loanIou, repaymentOrder.getBankCard(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, newAmount, withholdRate, repaymentOrder.getBathNo());
                log.info("继续拆分代扣，RepaymentNo:" + newRepaymentOrder.getRepaymentNo());
                this.doBatchRepayment(loanIou, newRepaymentOrder);
            }
        } else if (!BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH.equals(repaymentOrder.getActionType())) {
            //发送短信
            iRepaymentSmsService.activeRepayment(repaymentOrder,loanIou, "success");
        }
    }

    /**
     * 获取代扣比例
     * @param repaymentOrder
     * @return
     */
    private BigDecimal getwithholdRate(RepaymentOrder repaymentOrder) {
        BigDecimal withholdRate = BigDecimal.ZERO;
        Double[] splitWithholdingArray = BizConstants.SPLIT_WITHHOLDING_ARRAY;
        //查看原repyamentOrder批扣比例
        for (int i = 0; i < splitWithholdingArray.length; i++) {
            if (i != 0 && splitWithholdingArray[i] == repaymentOrder.getWithholdRate().doubleValue() && i + 1 < splitWithholdingArray.length) {
                withholdRate = new BigDecimal(splitWithholdingArray[i + 1]);
            }
        }
        return withholdRate;
    }

    /**
     * 处理异常，修改用户溢缴金额等
     * @param repaymentOrder
     * @param loanIou
     */
    private void dealUnusualIou(RepaymentOrder repaymentOrder, LoanIou loanIou) {
        log.info("接收到了webhook，但loaniou的状态异常:{}>>>>流水号为:{}", loanIou.getIouStatus(), loanIou.getLoanSerialNo());
        //借据已完成，将该款项设置为溢缴款
        LoanOverflow loanOverflow = new LoanOverflow();
        loanOverflow.setChangeAmount(repaymentOrder.getRepaymentAmt());
        loanOverflow.setCustNo(repaymentOrder.getCustNo());
        loanOverflow.setOverflowTime(repaymentOrder.getCreateTime());
        loanOverflow.setType(BizConstants.OVERFLOW_TYPE_REPEAT_WITHHOLD);
        loanOverflow.setRepaymentOrderNo(repaymentOrder.getRepaymentNo());
        loanOverflow.setLoanIouNo(repaymentOrder.getLoanSerialNo());
        iLoanOverflowService.insertLoanOverflow(loanOverflow);
        //并且将repaymentOrder设置为已完成
//            repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
//            repaymentOrderService.updateRepaymentOrder(repaymentOrder);

        //修改用户溢缴金额
        // creditAccountService.updateOverflowAmount(repaymentOrder.getCustNo(), repaymentOrder.getRepaymentAmt());
    }

    public void doBatchRepayment(LoanIou loan, RepaymentOrder r) {

        //查询需代扣的银行卡列表
        List<CustBankCard> bankList;
        String email = "";
        Map<String, Object> stringObjectMap = doBatchRepaymentBankList(r);
        bankList = (List<CustBankCard>) stringObjectMap.get("bankList");
        email = stringObjectMap.get("email") + "";
        log.info(bankList.size() + "!!!!!<<<<>>>>>>");
        doBatchRepayment(loan, r, bankList, email);
    }

    public void doBatchRepayment(LoanIou loan, RepaymentOrder r, List<CustBankCard> bankList, String email) {
        log.info("开始处理借据编号为:" + r.getLoanSerialNo() + "，扣款流水编号：" + r.getRepaymentNo() + "的批扣>>>>>>>");
        int splitIndex = 0;//拆分标记
        //拆分数组
        Double[] splitWithholdingArray = BizConstants.SPLIT_WITHHOLDING_ARRAY;

        //查看repyamentOrder批扣比例
        for (int i = 0; i < splitWithholdingArray.length; i++) {
            if (splitWithholdingArray[i] == r.getWithholdRate().doubleValue()) {
                splitIndex = i;
            }
        }

        //当次扣款对象
        RepaymentOrder src = r;

        //中共需要代扣的总金额
        //BigDecimal totalAmt = r.getRepaymentAmt();//为了兼容传进来的order为拆分代扣
        BigDecimal needTotal = loan.getLoanPrincipal().add(loan.getLoanInterest()).add(loan.getServiceFee()).add(loan.getLateFee());
        BigDecimal repaidTotal = loan.getRepaidPrincipal().add(loan.getRepaidInterest()).add(loan.getRepaidServiceFee()).add(loan.getRepaidLateFee());
        BigDecimal repaymentAmt = needTotal.subtract(repaidTotal);
        BigDecimal totalAmt = repaymentAmt;

        int batchNo = 0;//代扣批次号
        BigDecimal batchTotalAmt = totalAmt;//批次代扣总金额

        //BigDecimal batchRepaymentOrder = totalAmt;//当次代扣金额
        BigDecimal batchRepaymentOrder = r.getRepaymentAmt();//当次代扣金额
        //double amtProportion = 1;//当次代扣占比

        while (totalAmt.compareTo(BigDecimal.ZERO) == 1) {//没扣完
            log.info("总金额：" + batchTotalAmt + ",开始:" + batchNo + "批次，占比："
                    + splitWithholdingArray[splitIndex] + "，金额：" + batchRepaymentOrder + "的代扣>>>>>>>");
//
//            Boolean isFail = true;

//            if(batchNo==0 && splitIndex==1){
//                isFail = false;
//            }
//
//            if(batchNo==1 && splitIndex==1){
//                isFail = false;
//            }
//
//            if(batchNo==1 && splitIndex==2){
//                isFail = false;
//            }
//
//            if(batchNo==2){
//                isFail = false;
//            }

            Boolean isFail = true;
            try {
                log.info("email={}; bank={}", email, src.getBankCard());
                Map<String, Boolean> stringBooleanMap = loanInfoService.doingRepayment(src, email, src.getBankCard());
                isFail = stringBooleanMap.get("isFail");
                if (stringBooleanMap.get("isChangeCard")) {
                    splitIndex = 1;
                }
            } catch (Exception e) {
                log.error("isFail异常", e);
            }

            log.info("==========>>>><<<<" + isFail);

            if (isFail) {
                //代扣失败
                log.info("》》》》扣款失败占比：" + splitWithholdingArray[splitIndex]);
                if (splitIndex != 0) {//拆分代扣进入下一批次   当拆分代扣失败时，则换卡
                    //换卡再扣全额
                    if (batchNo < bankList.size()) {
                        CustBankCard bankCard = bankList.get(batchNo);
                        String bankNo = bankCard.getAuthorizationCode();
                        email = bankCard.getEmail();
                        batchNo = batchNo + 1; //批次加一
                        splitIndex = 0;

                        batchTotalAmt = totalAmt;
                        batchRepaymentOrder = totalAmt;
                        log.info("》》》》需要换卡再扣：" + batchTotalAmt);
                        src = this.splitRepaymentOrder(src, bankNo, batchRepaymentOrder, new BigDecimal(splitWithholdingArray[splitIndex]), src.getBathNo());
                    } else {
                        log.info("》》》》没卡需要更换了");
                        iRepaymentSmsService.batchWithhold(loan, r.getBathNo());
                        break;
                    }
                } else {//非拆分代扣进入非拆分代   当全量代扣失败时，则进入拆分代扣
                    log.info("》》》》需要进行拆分代扣");
                    //拆分第一个开始
                    splitIndex = 1;

                    //构建代扣对象
                    //amtProportion = splitArray.get(splitIndex);//每次金额占比
                    //当计算出来的金额大于还款总金额时   金额=总金额
                    batchRepaymentOrder = batchTotalAmt.multiply(new BigDecimal(splitWithholdingArray[splitIndex])).setScale(4, BigDecimal.ROUND_UP);
                    if (batchRepaymentOrder.doubleValue() > batchTotalAmt.doubleValue()) {
                        batchRepaymentOrder = batchTotalAmt;
                    }
                    src = this.splitRepaymentOrder(src, src.getBankCard(), batchRepaymentOrder, new BigDecimal(splitWithholdingArray[splitIndex]), src.getBathNo());
                }
            } else {//扣成功

                if (splitIndex == 0) {
                    //全量代扣成功
                    iRepaymentSmsService.batchWithhold(loan, r.getBathNo());
                    return;
                }

                totalAmt = totalAmt.subtract(batchRepaymentOrder);//更新总未还

                log.info("》》》》扣款成功占比：" + splitWithholdingArray[splitIndex]);

                boolean b = checkAmount(loan, "");
                if (!b) {
                    log.info("借据小于阀值，结清，LoanSerialNo:{}", loan.getLoanSerialNo());
                    return;
                }

                //if (1 > amtProportion && splitIndex < splitArray.size() - 1) {//进入下一比例代扣
                if (splitIndex < splitWithholdingArray.length - 1) {//进入下一比例代扣
                    splitIndex = splitIndex + 1;

                    //构建代扣对象
                    //amtProportion = splitArray.get(splitIndex);//每次金额占比
                    log.info("》》》》需要执行下一次占比：" + splitWithholdingArray[splitIndex]);
                    //当计算出来的金额大于还款总金额时   金额=总金额
                    batchRepaymentOrder = batchTotalAmt.multiply(new BigDecimal(splitWithholdingArray[splitIndex]));
                    if (batchRepaymentOrder.doubleValue() > batchTotalAmt.doubleValue()) {
                        batchRepaymentOrder = batchTotalAmt;
                    }
                    src = this.splitRepaymentOrder(src, src.getBankCard(), batchRepaymentOrder, new BigDecimal(splitWithholdingArray[splitIndex]), src.getBathNo());
                }
            }
        }
    }

    /**
     * 获取用户可用代扣的银行卡列表
     *
     * @param repaymentOrder
     */
    public Map<String, Object> doBatchRepaymentBankList(RepaymentOrder repaymentOrder) {
        //查找用户的银行卡集合
        CustBankCard bankCardquery = new CustBankCard();
        bankCardquery.setCustNo(repaymentOrder.getCustNo());
        bankCardquery.setState(UserConstants.BUSINESS_NORMAL);
        List<CustBankCard> list = custBankCardService.selectCustBankCardList(bankCardquery);
        //查找用户今天的repyamentOrder 并且状态为DateBatch  并且批次号一致
        List<RepaymentOrder> repaymentOrderList = repaymentMapper.getNowByType(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH, repaymentOrder.getBathNo(), repaymentOrder.getCustNo());
        //差集
        List<CustBankCard> bankList = list.stream().filter(item -> !repaymentOrderList.stream().map(e -> e.getBankCard()).collect(Collectors.toList()).contains(item.getAuthorizationCode())).collect(Collectors.toList());
        String email = "";
        for (CustBankCard custBankCard : list) {
            if (custBankCard.getAuthorizationCode().equals(repaymentOrder.getBankCard())) {
                email = custBankCard.getEmail();
            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("bankList", bankList);
        map.put("email", email);
        return map;
    }

    @Override
    public Integer getInRepaymentCountByLoan(String loanSerialNo) {
        Integer result = repaymentMapper.getInRepaymentCountByLoan(loanSerialNo);
        if (null == result) {
            result = 0;
        }
        return result;
    }

    @Override
    public Map<String, Object> createUSSD(LoanIou loanIou) {

        Map<String, Object> map = new HashMap<>();

        //是否存在ussd还款记录表
//		RepaymentOrder repaymentOrderSelect = new RepaymentOrder();
//		repaymentOrderSelect.setLoanSerialNo(loanIou.getLoanSerialNo());
//		repaymentOrderSelect.setActionType(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_USSD);
//		repaymentOrderSelect.setState(UserConstants.BUSINESS_NORMAL);
//		repaymentOrderSelect.setRepayStatus(2);
//		 repaymentOrderService.selectRepaymentOrderList(repaymentOrderSelect);
        List<RepaymentOrder> repaymentOrders = getToActionTypeAndLoanSerialNo(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_USSD, loanIou.getLoanSerialNo());

        if (repaymentOrders != null && repaymentOrders.size() > 0) {
            //如果存在，则查询是否有monnfiy_order

            ExtMonnifyOrder extMonnifyOrder = new ExtMonnifyOrder();
            extMonnifyOrder.setPaymentStatus("CREATE");
            extMonnifyOrder.setPaymentReference(repaymentOrders.get(0).getRepaymentNo());
            extMonnifyOrder.setState(UserConstants.BUSINESS_NORMAL);
            extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_USSD);
            List<ExtMonnifyOrder> extMonnifyOrders = iExtMonnifyOrderService.selectExtMonnifyOrderList(extMonnifyOrder);

            Object cacheObject = redisCache.getCacheObject("monnifyTransactionsInitTransaction:" + repaymentOrders.get(0).getRepaymentNo());

            //判断是否存在monnfiy_order数据 并且 redis存在 订单没过期
            if (extMonnifyOrders != null && extMonnifyOrders.size() > 0 && cacheObject != null) {

                map.put("paymentReference", extMonnifyOrders.get(0).getPaymentReference());
                map.put("time", extMonnifyOrders.get(0).getCreateTime());
                //直接返回paymentReference
                return map;
            } else {
                //没有则将repaymentOrders数据设置为还款失败
                repaymentOrders.get(0).setRepayStatus(0);
                repaymentOrderService.updateRepaymentOrder(repaymentOrders.get(0));
            }
        }

        //添加repayment_order记录
        RepaymentOrder repaymentOrder = initRepaymentOrderToLoanIou(loanIou, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_USSD, BigDecimal.ZERO);

        String orderSn = iExternalApiService.monnifyTransactionsInitTransaction(loanIou.getCustNo(), repaymentOrder.getShouldRepaymentAmt().setScale(2, BigDecimal.ROUND_CEILING), repaymentOrder.getRepaymentNo(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_USSD, new String[]{"ACCOUNT_TRANSFER"});

        repaymentOrder.setPayTransSerialNo(orderSn);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);

        //保存redis  20分钟
        redisCache.setCacheObject("monnifyTransactionsInitTransaction:" + repaymentOrder.getRepaymentNo(), repaymentOrder.getPayTransSerialNo());
        redisCache.expire("monnifyTransactionsInitTransaction:" + repaymentOrder.getRepaymentNo(), 20 * 60, TimeUnit.SECONDS);

        //添加monnify订单表
        ExtMonnifyOrder extMonnifyOrder = new ExtMonnifyOrder();
        extMonnifyOrder.setCustNo(loanIou.getCustNo());
        extMonnifyOrder.setPaymentReference(repaymentOrder.getRepaymentNo());
        extMonnifyOrder.setTransactionReference(orderSn);
        extMonnifyOrder.setPaymentStatus("CREATE");
        extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_USSD);

        iExtMonnifyOrderService.insertExtMonnifyOrder(extMonnifyOrder);

        map.put("paymentReference", repaymentOrder.getRepaymentNo());
        map.put("time", DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss"));
        return map;

    }

    @Override
    public String getUssdpaymentForBank(String transactionReference, String accountDetailsBankCode) {
        //查询ussdpayment
        String s = iExternalApiService.monnifyBankTransferInitPayment(getUserId(), transactionReference, accountDetailsBankCode);

        //mo_order表状态改为PENDING
        ExtMonnifyOrder extMonnifyOrder = new ExtMonnifyOrder();
        extMonnifyOrder.setTransactionReference(transactionReference);
        extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_USSD);
        List<ExtMonnifyOrder> extMonnifyOrders = iExtMonnifyOrderService.selectExtMonnifyOrderList(extMonnifyOrder);

        extMonnifyOrders.get(0).setPaymentStatus("PENDING");
        iExtMonnifyOrderService.updateExtMonnifyOrder(extMonnifyOrders.get(0));
        return s;
    }

    @Override
    public void getTransactionStatus(String paymentReference, String transactionReference) {

        if (!checkTransactionIsSuccess(paymentReference, transactionReference)) {
            TransactionThread transaction = new TransactionThread(paymentReference, transactionReference, redisCache, this);
            transaction.start();
        }
    }

    @Override
    public boolean checkTransactionIsSuccess(String paymentReference, String transactionReference) {
        //查询状态
        JSONObject jsonObject = iExternalApiService.monnifyGetTransactionStatus(transactionReference);

        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        repaymentOrder.setRepaymentNo(paymentReference);
        RepaymentOrder repaymentOrder1 = iRepaymentOrderService.selectRepaymentOrderList(repaymentOrder).get(0);


        //更新第三方订单表
        ExtMonnifyOrder extMonnifyOrder = new ExtMonnifyOrder();
        extMonnifyOrder.setTransactionReference(transactionReference);
        extMonnifyOrder.setState(UserConstants.BUSINESS_NORMAL);
        if (BizConstants.LOAN_REPAYMENT_ORDER_TYPE_CARD.equals(repaymentOrder1.getActionType())) {
            extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_CARD);
        } else if (BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS.equals(repaymentOrder1.getActionType())) {
            extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_TTS);
        } else {
            extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_USSD);
        }

        extMonnifyOrder = iExtMonnifyOrderService.selectExtMonnifyOrderList(extMonnifyOrder).get(0);


        if ("PAID".equals(jsonObject.getString("paymentStatus"))) {

            extMonnifyOrder.setPaymentStatus(jsonObject.getString("paymentStatus"));
            if (jsonObject.getJSONObject("metaData") != null) {
                extMonnifyOrder.setMetaDataName(jsonObject.getJSONObject("metaData").getString("name"));
                extMonnifyOrder.setMetaDataAge(jsonObject.getJSONObject("metaData").getString("age"));
            }

            if (jsonObject.getJSONObject("customer") != null) {
                extMonnifyOrder.setCustomerEmail(jsonObject.getJSONObject("customer").getString("email"));
                extMonnifyOrder.setCustomerName(jsonObject.getJSONObject("customer").getString("name"));
            }

            if (jsonObject.getJSONObject("accountDetails") != null) {
                extMonnifyOrder.setAccountDetailsAccountName(jsonObject.getJSONObject("accountDetails").getString("accountName"));
                extMonnifyOrder.setAccountDetailsAccountNumber(jsonObject.getJSONObject("accountDetails").getString("accountNumber"));
                extMonnifyOrder.setAccountDetailsBankCode(jsonObject.getJSONObject("accountDetails").getString("bankCode"));
                extMonnifyOrder.setAccountDetailsAmountPaid(jsonObject.getJSONObject("accountDetails").getString("amountPaid"));
            }

            extMonnifyOrder.setCardDetails(jsonObject.getString("cardDetails"));
            extMonnifyOrder.setProductType(jsonObject.getJSONObject("product").getString("type"));
            extMonnifyOrder.setProductReference(jsonObject.getJSONObject("product").getString("reference"));
            extMonnifyOrder.setPaymentMethod(jsonObject.getString("paymentMethod"));
            extMonnifyOrder.setCurrency(jsonObject.getString("currency"));
            extMonnifyOrder.setPaymentDescription(jsonObject.getString("paymentDescription"));
            extMonnifyOrder.setPaidOn(jsonObject.getString("paidOn"));
            extMonnifyOrder.setSettlementAmount(jsonObject.getBigDecimal("settlementAmount"));
            extMonnifyOrder.setTotalPayable(jsonObject.getBigDecimal("totalPayable"));
            extMonnifyOrder.setAmountPaid(jsonObject.getBigDecimal("amountPaid"));

            Date time;
            try {
                time = DateUtils.dateFormatConversion("dd/MM/yyyy HH:mm:ss a", jsonObject.getString("paidOn"));
            } catch (Exception e) {
                throw new BusinessException(RspEnmu.SYSTEM_ERR);
            }

            repaymentOrder1.setPayTransSerialNo(extMonnifyOrder.getTransactionReference());
            JSONObject jsonObjectForMq = createJsonData(extMonnifyOrder,repaymentOrder1, jsonObject.getBigDecimal("amountPaid"), time, "TTS-Getstatus");

             repaymentMqService.syncSendMqRepayMentLunxun(jsonObjectForMq,repaymentOrder1.getCustNo());


            //transactionStatusSuccess(repaymentOrder1, jsonObject.getBigDecimal("amountPaid"), time, "TTS-Getstatus");

            //iExtMonnifyOrderService.updateExtMonnifyOrder(extMonnifyOrder);
            return true;
        } else {
            extMonnifyOrder.setPaymentStatus(jsonObject.getString("paymentStatus"));
            //判断当前时间
            Date startOfDate = Calendar.getInstance().getTime();
            Calendar calendar2 = Calendar.getInstance();
            calendar2.set(calendar2.get(Calendar.YEAR), calendar2.get(Calendar.MONTH), calendar2.get(Calendar.DAY_OF_MONTH),
                    23, 00, 00);
            Date endOfDate = calendar2.getTime();

            iExtMonnifyOrderService.updateExtMonnifyOrder(extMonnifyOrder);
            //如果当前时间小于23点之前
            return startOfDate.before(endOfDate);
        }
    }

    private JSONObject createJsonData(ExtMonnifyOrder extMonnifyOrder,RepaymentOrder repaymentOrder, BigDecimal amountPaid, Date time, String reduceSource) {
        JSONObject res = new JSONObject();
        res.put("extMonnifyOrder",extMonnifyOrder);
        res.put("repaymentOrder",repaymentOrder);
        res.put("amountPaid",amountPaid);
        res.put("time",time);
        res.put("reduceSource",reduceSource);
        return res;
    }

    @Override
    public Map createTransferToUs(LoanIou loanIou) {

        //判断ext_monnify_reserved_accounts里有无数据
//        ExtMonnifyReservedAccounts extMonnifyReservedAccounts = new ExtMonnifyReservedAccounts();
//        extMonnifyReservedAccounts.setAccountReference(loanIou.getCustNo());
//        extMonnifyReservedAccounts.setState(UserConstants.BUSINESS_NORMAL);
//        List<ExtMonnifyReservedAccounts> extMonnifyReservedAccounts1 = iExtMonnifyReservedAccountsService.selectExtMonnifyReservedAccountsList(extMonnifyReservedAccounts);
//        //有则直接返回
//        if (extMonnifyReservedAccounts1 != null && extMonnifyReservedAccounts1.size() > 0) {
//
//            //查询是否有repaymentOrder
//            List<RepaymentOrder> toActionTypeAndLoanSerialNo = getToActionTypeAndLoanSerialNo(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS, loanIou.getLoanSerialNo());
//
//            if (toActionTypeAndLoanSerialNo == null || toActionTypeAndLoanSerialNo.size() == 0) {
//                //添加数据
//                RepaymentOrder repaymentOrder = initRepaymentOrderToLoanIou(loanIou, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS, BigDecimal.ZERO);
//                repaymentOrder.setPayTransSerialNo(loanIou.getCustNo());
//                repaymentOrderService.insertRepaymentOrder(repaymentOrder);
//            }
//
//            Map<String, String> map = new HashMap<>();
//            map.put("accountNumber", extMonnifyReservedAccounts1.get(0).getAccountsAccountNumber());
//            map.put("custNo", extMonnifyReservedAccounts1.get(0).getAccountReference());
//            map.put("bank", extMonnifyReservedAccounts1.get(0).getAccountsBankName());
//            map.put("accountName", extMonnifyReservedAccounts1.get(0).getAccountName().replace("Link Credit -", ""));
//            return map;
//        }

        //请求接口，创建虚拟账户
//		String userId = getUserId();
//
//		RealNameAuth realNameAuth = new RealNameAuth();
//		realNameAuth.setState(UserConstants.BUSINESS_NORMAL);
//		realNameAuth.setCustNo(loanIou.getCustNo());
//		realNameAuth = iRealNameAuthService.selectRealNameAuthList(realNameAuth).get(0);
//
//		String accountName = realNameAuth.getFirstName()+"-LinkCredit/Monnify";
//		String bvn = realNameAuth.getBvnAccount();
//		String email = userId.substring(userId.length() - 10) + "@LCserver.com";
//		String custName = realNameAuth.getFirstName()+" "+realNameAuth.getMiddleName()+" "+realNameAuth.getLastName();
//		IExternalApiService.monnifyBankTransferReservedAccounts(accountName,bvn,email,custName,loanIou.getCustNo(),token);

        ExtMonnifyReservedAccounts extMonnifyReservedAccountsAdd = checkAccountReference(loanIou.getCustNo());

        List<RepaymentOrder> toActionTypeAndLoanSerialNo = getToActionTypeAndLoanSerialNo(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS, loanIou.getLoanSerialNo());

        if (toActionTypeAndLoanSerialNo == null || toActionTypeAndLoanSerialNo.size() == 0) {
            //添加数据
            RepaymentOrder repaymentOrder = initRepaymentOrderToLoanIou(loanIou, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS, BigDecimal.ZERO);
            repaymentOrder.setPayTransSerialNo(loanIou.getCustNo());
            repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        }

        //添加还款记录
//        RepaymentOrder repaymentOrder = initRepaymentOrderToLoanIou(loanIou, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS, BigDecimal.ZERO);
//        repaymentOrder.setPayTransSerialNo(loanIou.getCustNo());
//        repaymentOrderService.insertRepaymentOrder(repaymentOrder);

        Map<String, String> map = new HashMap<>();
        map.put("accountNumber", extMonnifyReservedAccountsAdd.getAccountsAccountNumber());
        map.put("custNo", extMonnifyReservedAccountsAdd.getAccountReference());
        map.put("bank", extMonnifyReservedAccountsAdd.getAccountsBankName());
        map.put("accountName", extMonnifyReservedAccountsAdd.getAccountName().replace("Link Credit -", ""));
        return map;
    }

    @Override
    public void submitTransferToUs(RepaymentTransfer repaymentTransfer) {

        //添加repaymentTransfer
        iRepaymentTransferService.insertRepaymentTransfer(repaymentTransfer);

        // 注释原因：2022-08-01 上新的VA 不再创建还款订单，就不需要3小时轮询
//        //找到2的
//        RepaymentOrder repaymentOrder = new RepaymentOrder();
//        repaymentOrder.setLoanSerialNo(repaymentTransfer.getLoanSerialNo());
//        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
//        repaymentOrder.setActionType(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_TTS);
//        repaymentOrder.setRepayStatus(2);
//        List<RepaymentOrder> toActionTypeAndLoanSerialNo = iRepaymentOrderService.selectRepaymentOrderList(repaymentOrder);
//        if (toActionTypeAndLoanSerialNo != null && toActionTypeAndLoanSerialNo.size() > 0) {
//            toActionTypeAndLoanSerialNo.get(0).setRepayStatus(5);
//            iRepaymentOrderService.updateRepaymentOrder(toActionTypeAndLoanSerialNo.get(0));
//        }
//        //getToActionTypeAndLoanSerialNo(, repaymentTransfer.getLoanSerialNo());
//        //closeByRepayemntNo(toActionTypeAndLoanSerialNo.get(0).getRepaymentNo());
//
//        boolean polling = getAllReservedAccountOrder(repaymentTransfer.getCustNo());
//
//        //开启轮询
//        if (polling) {
//            //判断当前时间
//            Date startOfDate = Calendar.getInstance().getTime();
//            Calendar calendar2 = Calendar.getInstance();
//            calendar2.set(calendar2.get(Calendar.YEAR), calendar2.get(Calendar.MONTH), calendar2.get(Calendar.DAY_OF_MONTH),
//                    23, 00, 00);
//            Date endOfDate = calendar2.getTime();
//
//            //如果当前时间小于23点之前
//            if (startOfDate.before(endOfDate)) {
//                TransferToUsThread transferToUs = new TransferToUsThread(repaymentTransfer.getCustNo(), redisCache, this);
//                transferToUs.start();
//            }
//        }
    }

    @Override
    public ExtMonnifyReservedAccounts checkAccountReference(String custNo) {

        String userId = getAppUserByCustNo(custNo).getUserUuid();

        ExtMonnifyReservedAccounts extMonnifyReservedAccount = new ExtMonnifyReservedAccounts();
        extMonnifyReservedAccount.setUsrId(userId);
        extMonnifyReservedAccount.setState(UserConstants.BUSINESS_NORMAL);
        List<ExtMonnifyReservedAccounts> extMonnifyReservedAccountList = iExtMonnifyReservedAccountsService.selectExtMonnifyReservedAccountsList(extMonnifyReservedAccount);
        if (extMonnifyReservedAccountList.size() != 0) {
            return extMonnifyReservedAccountList.get(0);
        }

        JSONObject responseBody;
        JSONObject jsonObject = iExternalApiService.monnifyGetBankTransferReservedAccounts(custNo);
        String responseCode = jsonObject.getString("responseCode");

        if ("0".equals(responseCode)) {
            //如果存在
            responseBody = jsonObject.getJSONObject("responseBody");
        } else {
            String responseMessage = jsonObject.getString("responseMessage");

            String monnifyNotFindReservedAccount = iSysConfigService.selectConfigByKey("monnify_not_find_reserved_account");
            if (StringUtils.isBlank(monnifyNotFindReservedAccount)) {
                monnifyNotFindReservedAccount = "Cannot find reserved account";
            }

            if (monnifyNotFindReservedAccount.equals(responseMessage)) {
                //不存在该用户
                RealNameAuth realNameAuth = new RealNameAuth();
                realNameAuth.setState(UserConstants.BUSINESS_NORMAL);
                realNameAuth.setCustNo(custNo);
                realNameAuth = iRealNameAuthService.selectRealNameAuthList(realNameAuth).get(0);

                String accountName = realNameAuth.getFirstName() + "-LinkCredit/Monnify";
                String bvn = realNameAuth.getBvnAccount();
                String email = realNameAuth.getUserId().substring(realNameAuth.getUserId().length() - 10) + "@LCserver.com";
                String custName = realNameAuth.getFirstName() + " " + realNameAuth.getMiddleName() + " " + realNameAuth.getLastName();
                responseBody = (JSONObject) retryFunction.tryCount(e -> iExternalApiService.monnifyBankTransferReservedAccounts(accountName, bvn, email, custName, custNo), realNameAuth.getUserId(), ExtTryQueue.VIRTUAL);

            } else {
                throw new BusinessException(responseMessage, RspEnmu.MONNIFY_INTERFACE_ERROR);
            }
        }
        ExtMonnifyReservedAccounts extMonnifyReservedAccountsAdd = new ExtMonnifyReservedAccounts();
        extMonnifyReservedAccountsAdd.setUsrId(userId);
        extMonnifyReservedAccountsAdd.setState(UserConstants.BUSINESS_NORMAL);
        List<ExtMonnifyReservedAccounts> extMonnifyReservedAccounts = iExtMonnifyReservedAccountsService.selectExtMonnifyReservedAccountsList(extMonnifyReservedAccountsAdd);

        extMonnifyReservedAccountsAdd = new ExtMonnifyReservedAccounts();
        extMonnifyReservedAccountsAdd.setUsrId(userId);
        extMonnifyReservedAccountsAdd.setContractCode(responseBody.getString(("contractCode")));
        extMonnifyReservedAccountsAdd.setAccountReference(responseBody.getString(("accountReference")));
        extMonnifyReservedAccountsAdd.setAccountName(responseBody.getString(("accountName")));
        extMonnifyReservedAccountsAdd.setCurrencyCode(responseBody.getString(("currencyCode")));
        extMonnifyReservedAccountsAdd.setCustomerEmail(responseBody.getString(("customerEmail")));
        extMonnifyReservedAccountsAdd.setCustomerName(responseBody.getString(("customerName")));
        if (responseBody.getJSONArray("accounts") != null && responseBody.getJSONArray("accounts").size() > 0) {
            JSONObject accounts = JSONObject.parseObject(responseBody.getJSONArray("accounts").get(0).toString());
            extMonnifyReservedAccountsAdd.setAccountsBankCode(accounts.getString("bankCode"));
            extMonnifyReservedAccountsAdd.setAccountsBankName(accounts.getString("bankName"));
            extMonnifyReservedAccountsAdd.setAccountsAccountNumber(accounts.getString("accountNumber"));
            extMonnifyReservedAccountsAdd.setAccountsAccountName(accounts.getString("accountName"));
        }
        extMonnifyReservedAccountsAdd.setCollectionChannel(responseBody.getString(("collectionChannel")));
        extMonnifyReservedAccountsAdd.setReservationReference(responseBody.getString(("reservationReference")));
        extMonnifyReservedAccountsAdd.setReservedAccountType(responseBody.getString(("reservedAccountType")));
        extMonnifyReservedAccountsAdd.setStatus(responseBody.getString(("status")));
        extMonnifyReservedAccountsAdd.setCreatedOn(responseBody.getString(("createdOn")));
        extMonnifyReservedAccountsAdd.setBvn(responseBody.getString(("bvn")));
        extMonnifyReservedAccountsAdd.setRestrictPaymentSource(responseBody.getString(("restrictPaymentSource")));

        if (extMonnifyReservedAccounts != null && extMonnifyReservedAccounts.size() > 0) {
            extMonnifyReservedAccountsAdd.setId(extMonnifyReservedAccounts.get(0).getId());
            iExtMonnifyReservedAccountsService.updateExtMonnifyReservedAccounts(extMonnifyReservedAccountsAdd);
        } else {
            //保存ext_monnify_reserved_accounts表
            iExtMonnifyReservedAccountsService.insertExtMonnifyReservedAccounts(extMonnifyReservedAccountsAdd);
        }

        return extMonnifyReservedAccountsAdd;
    }

    @Override
    public boolean getAllReservedAccountOrder(String custNo) {

        QueryWrapper<ExtMonnifyReservedAccounts> accountsQueryWrapper = new QueryWrapper<>();
        accountsQueryWrapper.eq("cust_no", custNo).eq("state", UserConstants.BUSINESS_NORMAL).select("cust_no", "account_reference");
        List<ExtMonnifyReservedAccounts> monnifyReservedAccounts = iExtMonnifyReservedAccountsService.list(accountsQueryWrapper);
        if(CollectionUtils.isEmpty(monnifyReservedAccounts)) {
            log.error("查询到的虚拟账户为空，custNo：{}", custNo);
            return false;
        }

        //是否需要开启轮训
        boolean polling = true;

        for (ExtMonnifyReservedAccounts reservedAccounts : monnifyReservedAccounts) {
            String accountReference = reservedAccounts.getAccountReference();
            log.debug("待处理 accountReference：{}", accountReference);

            //获取全部订单
            int page = 0;
            int size = 10;

            boolean isSuccess = true;
            while (isSuccess) {
                Map<String, Boolean> allReservedAccountOrder = getReservedAccountOrderPage(page, size, accountReference, custNo);
                isSuccess = !allReservedAccountOrder.get("last");
                if (!allReservedAccountOrder.get("polling")) {
                    polling = false;
                }
                page++;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    log.error("getAllReservedAccountOrder error:", e);
                }
            }
        }
        return polling;
    }

    @Override
    public JSONObject createCard(LoanIou loanIou) {

        //返回前端需要的参数
        //校验是否实名
        String userId = getUserId();
        RealNameAuth realNameAuth = new RealNameAuth();
        realNameAuth.setCustNo(loanIou.getCustNo());
        realNameAuth.setState(UserConstants.BUSINESS_NORMAL);
        List<RealNameAuth> realNameAuths = iRealNameAuthService.selectRealNameAuthList(realNameAuth);
        if (realNameAuths != null && realNameAuths.size() > 0) {

            RepaymentOrder repaymentOrder = initRepaymentOrderToLoanIou(loanIou, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_CARD, BigDecimal.ZERO);

            repaymentOrderService.insertRepaymentOrder(repaymentOrder);

            JSONObject json = new JSONObject();
            json.put("amount", repaymentOrder.getShouldRepaymentAmt().doubleValue());
            json.put("customerName", realNameAuths.get(0).getFirstName() + " " + realNameAuths.get(0).getMiddleName() + " " + realNameAuths.get(0).getLastName());
            json.put("customerEmail", userId.substring(userId.length() - 10) + "@LCserver.com");
            json.put("reference", repaymentOrder.getRepaymentNo());
            json.put("paymentDescription", "Card");
            json.put("currency", "NGN");
            json.put("customerMobileNumber", realNameAuths.get(0).getPhoneNumber());
            json.put("apiKey", monnifyConfig.getApikey());
            json.put("contractCode", monnifyConfig.getContractCode());
            json.put("paymentMethods", new String[]{"CARD"});

            return json;
        }
        throw new BusinessException("custNo=" + loanIou.getCustNo(), RspEnmu.CUST_IS_NULL_NEED_REALNAME_AUTH);

    }


    @Override
    public void submitUssd(String paymentReference, String transactionReference) {

        //将repaymentOrder改为5
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(paymentReference);
        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        repaymentOrder.setActionType(BizConstants.LOAN_REPAYMENT_ORDER_TYPE_USSD);
        repaymentOrder.setRepayStatus(2);
        List<RepaymentOrder> toActionTypeAndLoanSerialNo = iRepaymentOrderService.selectRepaymentOrderList(repaymentOrder);
        if (toActionTypeAndLoanSerialNo != null && toActionTypeAndLoanSerialNo.size() > 0) {
            toActionTypeAndLoanSerialNo.get(0).setRepayStatus(5);
            iRepaymentOrderService.updateRepaymentOrder(toActionTypeAndLoanSerialNo.get(0));
        }

        getTransactionStatus(paymentReference, transactionReference);
    }

    @Override
    public void submitCard(RepaymentOrder repaymentOrder, String paymentReference, String transactionReference) {

        //更新repaymentOrder
        repaymentOrder.setPayTransSerialNo(transactionReference);
        repaymentOrder.setRepayStatus(5);
        repaymentOrderService.updateRepaymentOrder(repaymentOrder);


        //查找有无ExtMonnifyOrder记录
        ExtMonnifyOrder extMonnifyOrder = new ExtMonnifyOrder();
        extMonnifyOrder.setPaymentStatus("CREATE");
        extMonnifyOrder.setPaymentReference(paymentReference);
        extMonnifyOrder.setState(UserConstants.BUSINESS_NORMAL);
        extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_CARD);
        List<ExtMonnifyOrder> extMonnifyOrders = iExtMonnifyOrderService.selectExtMonnifyOrderList(extMonnifyOrder);
        if (extMonnifyOrders == null || extMonnifyOrders.size() < 1) {
            //添加ExtMonnifyOrder
            extMonnifyOrder = new ExtMonnifyOrder();
            extMonnifyOrder.setCustNo(repaymentOrder.getCustNo());
            extMonnifyOrder.setPaymentReference(paymentReference);
            extMonnifyOrder.setTransactionReference(transactionReference);
            extMonnifyOrder.setPaymentStatus("CREATE");
            extMonnifyOrder.setType(BizConstants.MONNFIY_ORDER_TYPE_CARD);

            iExtMonnifyOrderService.insertExtMonnifyOrder(extMonnifyOrder);
        }


        getTransactionStatus(paymentReference, transactionReference);
    }

    public Map<String, Boolean> getReservedAccountOrderPage(int page, int size, String accountReference, String custNo) {

        JSONObject jsonObject = null;
        Boolean polling = true;

        boolean isMock = Integer.parseInt(getDictLabel("biz_switch", "api_paystack_mock")) == 1;
        if (isMock) {
            jsonObject = JSONObject.parseObject(iSysConfigService.selectConfigByKey("MonniflyResfor|"+accountReference));
            log.info("外部接口MonnifyResforMock {} 返回模拟报文：{}", jsonObject.toJSONString());
        }else{
            jsonObject = iExternalApiService.monnifyGetAllTransactionsOnReservedAccount(accountReference, page, size);
        }

        JSONArray content = jsonObject.getJSONArray("content");
        for (int i = 0; i < content.size(); i++) {
            ExtMonnifyOrder extMonnifyOrder = monnifyRepaymentService.checkInterfaceStatus(content.getJSONObject(i), accountReference, custNo);
            if (extMonnifyOrder != null) {
                repaymentMqService.monnifyLunxun(extMonnifyOrder);
                //monnifyRepaymentService.repaymentLock(extMonnifyOrder);
                polling = false;
            }
        }

        Map<String, Boolean> map = new HashMap<>();
        map.put("last", jsonObject.getBoolean("last"));
        map.put("polling", polling);

        return map;
    }

    @Override
    public RepaymentOrder initRepaymentOrderToLoanIou(LoanIou loanIou, String actionType, BigDecimal amount) {

        RepaymentOrder repaymentOrder = new RepaymentOrder();

        String no = IdUtils.reqId();

        repaymentOrder.setRepaymentNo(no);
        repaymentOrder.setCustNo(loanIou.getCustNo());
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());
        repaymentOrder.setActionType(actionType);
        repaymentOrder.setLoanPrincipal(loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()));
        repaymentOrder.setServiceFee(loanIou.getServiceFee().subtract(loanIou.getRepaidServiceFee()));
        repaymentOrder.setLoanInterest(loanIou.getLoanInterest().subtract(loanIou.getRepaidInterest()));
        repaymentOrder.setLateFee(loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()));
        repaymentOrder.setReduceFee(loanIou.getFeeRemissionAmt());
        repaymentOrder.setShouldRepaymentAmt(repaymentOrder.getLoanPrincipal().add(repaymentOrder.getServiceFee()).add(repaymentOrder.getLoanInterest().add(repaymentOrder.getLateFee())).subtract(repaymentOrder.getReduceFee()));
        repaymentOrder.setRepaymentAmt(amount);
        repaymentOrder.setRepayStatus(2);

        return repaymentOrder;

    }

    @Override
    public void transactionStatusSuccess(RepaymentOrder repaymentOrder, BigDecimal amountPaid, Date payTime, String reduceSource) {
        //如果成功，更新repaymentOrder表并执行冲账

        // amountPaid = RepaymentUtils.setRepaymentAmount(amountPaid, repaymentOrder);

        repaymentOrder.setRepaymentAmt(amountPaid);
        repaymentOrder.setRepayStatus(1);
        repaymentOrder.setPayTime(payTime);

        iRepaymentOrderService.updateRepaymentOrder(repaymentOrder);

        String repayToWalletSwitch = sysDictDataService.selectDictLabel("biz_switch", "repay_to_wallet_switch");
        if(StringUtils.equals(BizConstants.BIZ_SWITCH_OPEN, repayToWalletSwitch)) {
            log.info("Monnify VA 还款金额直接充值钱包开关开启，不进入还款");
            TransferVo transferVo = TransferVo.builder().custNo(repaymentOrder.getCustNo()).transferAmt(amountPaid).payTime(payTime).actionType(BizConstants.OVERFLOW_TYPE_MONNIFY_CHARGE).reference(repaymentOrder.getPayTransSerialNo()).build();
            repayToWalletService.addOverflowAndPushWallet(transferVo);
            return;
        }

        //冲账
        iRepaymentService.repayment(repaymentOrder);

        //全部充完  检查这一笔loan_iou金额
        LoanIou loanIou = new LoanIou();
        loanIou.setLoanSerialNo(repaymentOrder.getLoanSerialNo());
        List<LoanIou> loanIous = iLoanIouService.selectLoanIouList(loanIou);
        checkAmountLock(loanIous.get(0), reduceSource);
    }

    @Override
    // @Transactional 发送mq不能有事物
    public void overflowRepayment(RepaymentOrder repaymentOrder1, CreditAccount creditAccount) {
        creditAccount = creditAccountService.selectCreditAccountById(creditAccount.getId());
        BigDecimal amountPaid = creditAccount.getOverflowAmount()
                .subtract(creditAccount.getFrozenOverflowAmount())
                .divide(BigDecimal.TEN)
                .setScale(0, BigDecimal.ROUND_DOWN)
                .multiply(BigDecimal.TEN);
        log.info("amountPaid:getOverflowAmount:"+creditAccount.getOverflowAmount());
        log.info("amountPaid:getFrozenOverflowAmount:"+creditAccount.getFrozenOverflowAmount());

        log.info("amountPaid-jian:"+creditAccount.getOverflowAmount()
                .subtract(creditAccount.getFrozenOverflowAmount()));

        log.info("amountPaid:"+amountPaid);

        //更新repaymentOrder表
        amountPaid = RepaymentUtils.setRepaymentAmount(amountPaid, repaymentOrder1);
        log.info("amountPaid2:"+amountPaid);
        repaymentOrder1.setRepaymentAmt(repaymentOrder1.getRepaidLateFee().add(repaymentOrder1.getRepaidServiceFee().add(repaymentOrder1.getRepaidInterest().add(repaymentOrder1.getRepaidPrincipal()))));
        repaymentOrder1.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        repaymentOrder1.setPayTime(DateUtils.getNowDate());
        iRepaymentOrderService.insertRepaymentOrder(repaymentOrder1);

        if (creditAccount.getFrozenOverflowAmount() == null) {
            creditAccount.setFrozenOverflowAmount(BigDecimal.ZERO);
        }

        if (creditAccount.getOverflowAmount() == null) {
            creditAccount.setOverflowAmount(BigDecimal.ZERO);
        }

        //修改用户冻结溢缴款
        creditAccountService.updateFrozenCreditAccount(creditAccount.getCustNo(), repaymentOrder1.getRepaymentAmt());
        // 发送mq
        repaymentMqService.overflowRepaymentMq(repaymentOrder1,creditAccount.getId());
    }

    @Override
    public void overflowRepaymentMq(RepaymentOrder repaymentOrder1,long creditAccountId) {
        CreditAccount creditAccount = creditAccountService.selectCreditAccountById(creditAccountId);
        if (!creditAccount.getOnLoan().equals(BizConstants.ACCOUNT_ON_lOAN_STATUS_1)) {
            log.info("creditAccount:OnLoan==1"+creditAccount.getCustNo());
            return;
        }
        repaymentOrder1 = iRepaymentOrderService.getDetail(repaymentOrder1.getRepaymentNo());
        if(null!=repaymentOrder1 && repaymentOrder1.getRepayStatus()==BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS){
            log.info("repaymentOrder.getRepayStatus():"+repaymentOrder1.getRepayStatus());
            return;
        }
        BigDecimal amountPaid = creditAccount.getOverflowAmount()
                .subtract(creditAccount.getFrozenOverflowAmount())
                .divide(BigDecimal.TEN)
                .setScale(0, BigDecimal.ROUND_DOWN)
                .multiply(BigDecimal.TEN);
        log.info("amountPaid:getOverflowAmount:"+creditAccount.getOverflowAmount());
        log.info("amountPaid:getFrozenOverflowAmount:"+creditAccount.getFrozenOverflowAmount());

        log.info("amountPaid-jian:"+creditAccount.getOverflowAmount()
                .subtract(creditAccount.getFrozenOverflowAmount()));

        log.info("amountPaid:"+amountPaid);
        log.info("溢缴款MQ还款冲账：{},{},{}",repaymentOrder1.toString(),creditAccount.toString(),amountPaid);

        repaymentOrder1.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
        repaymentOrder1.setPayTime(DateUtils.getNowDate());
        iRepaymentOrderService.updateRepaymentOrder(repaymentOrder1);
        //冲账
        iRepaymentService.repaymentNoLock(repaymentOrder1);
        //修改用户溢缴款
        creditAccountService.updateOverflow(creditAccount.getCustNo(), repaymentOrder1.getRepaymentAmt().negate(), repaymentOrder1.getRepaymentAmt().negate());
        //新增溢缴款记录
        LoanOverflow loanOverflow = new LoanOverflow();
        loanOverflow.setRepaymentOrderNo(repaymentOrder1.getRepaymentNo());
        loanOverflow.setLoanIouNo(repaymentOrder1.getLoanSerialNo());
        loanOverflow.setType(BizConstants.OVERFLOW_TYPE_OVERFLOW_REPAYMENT);
        loanOverflow.setChangeType(BizConstants.OVERFLOW_CHANGE_TYPE_EXPENSE);
        loanOverflow.setCustNo(repaymentOrder1.getCustNo());
        loanOverflow.setChangeAmount(repaymentOrder1.getRepaymentAmt().negate());
        iLoanOverflowService.insertLoanOverflow(loanOverflow);

        creditAccount.setOverflowAmount(amountPaid);

        if (amountPaid.doubleValue() > 0) {

            List<LoanIou> loanIouList = loanInfoService.getFirstByCustNo(repaymentOrder1.getCustNo());
            if (loanIouList != null && loanIouList.size() > 0) {
                //新增repaymentOrder
                RepaymentOrder repaymentOrder2 = initRepaymentOrderToLoanIou(loanIouList.get(0), repaymentOrder1.getActionType(), BigDecimal.ZERO);
                overflowRepayment(repaymentOrder2, creditAccount);
            }
        }
    }


    @Override
    public void transactionStatusSuccess(LoanIou loanIou, RepaymentOrder repaymentOrder1, BigDecimal amountPaid, Date time, String reduceSource) {
        //如果成功，更新repaymentOrder表并执行冲账

        //更新repaymentOrder表
        amountPaid = RepaymentUtils.setRepaymentAmount(amountPaid, repaymentOrder1);

        repaymentOrder1.setRepaymentAmt(repaymentOrder1.getRepaidLateFee().add(repaymentOrder1.getRepaidServiceFee().add(repaymentOrder1.getRepaidInterest().add(repaymentOrder1.getRepaidPrincipal().add(amountPaid)))));
        repaymentOrder1.setRepayStatus(1);
        repaymentOrder1.setPayTime(time);

        iRepaymentOrderService.updateRepaymentOrder(repaymentOrder1);

        //冲账
        iRepaymentService.repayment(loanIou, repaymentOrder1.getRepaymentNo(), repaymentOrder1.getActionType(), repaymentOrder1.getRepaymentAmt(), repaymentOrder1.getReduceFee(), 1);

        //全部充完  检查这一笔loan_iou金额
        loanIou = new LoanIou();
        loanIou.setLoanSerialNo(loanIou.getLoanSerialNo());
        List<LoanIou> loanIous = iLoanIouService.selectLoanIouList(loanIou);
        checkAmountLock(loanIous.get(0), reduceSource);
    }

    @Override
    public LoanIou submitOffline(LoanIou loanIou, Date payTime, BigDecimal amount, String reqParam) {

        //将loanIou记录备份
        iBusinessService.bankUpLoanInfo(loanIou);

        //应该回滚的天数 6.28-7.08
        Date nowDate = DateUtils.getNowDate();
        int backDays;
        try {
            backDays = DateUtils.daysBetween(payTime, nowDate);
        } catch (Exception e) {
            throw new BusinessException(RspEnmu.SYSTEM_ERR);
        }
        log.info("记录晚提交了{}天", backDays);

        if (backDays < 0) {
            throw new BusinessException(RspEnmu.EXTERNAL_API_RSP_FALSE);
        }

        //真实逾期天数 6.20-6.28
        int overdueDay;
        try {
            overdueDay = DateUtils.daysBetween(DateUtils.parseDate(loanIou.getRepayDate()), payTime);
        } catch (Exception e) {
            throw new BusinessException(RspEnmu.SYSTEM_ERR);
        }
        log.info("真实逾期天数{}天", overdueDay);


        //逾期上限值计算
        if (backDays > 0) {
            if (overdueDay + backDays > loanIou.getLateFeeMaxDays()) {
                //真实+回滚大于25
                if (overdueDay >= loanIou.getLateFeeMaxDays()) {
                    //真实大于等于25 回滚=0
                    backDays = 0;
                } else {
                    backDays = loanIou.getLateFeeMaxDays() - overdueDay;
                }
            }
        }


        BigDecimal deductionAmount = BigDecimal.ZERO;
        if (backDays > 0) {
            //回滚天数大于0
            if (overdueDay <= 0) {
                //真实未逾期，减免金额=全部滞纳金
                deductionAmount = loanIou.getLateFee();

                //如果真实逾期天数<0    也就是说  当时还款还未逾期  所以backDay应该减去逾期天数=新滞纳金天数
                if (overdueDay < 0) {
                    backDays = backDays + overdueDay;
                }
            } else {
                //真实已逾期，减免金额=(本金-已还本金)*回滚天数*滞纳率
                deductionAmount = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()).multiply(new BigDecimal(backDays)).multiply(loanIou.getDateLateFee());
            }
        }

        log.info("回滚{}天", backDays);
        log.info("减免金额{}", deductionAmount);
        //如果还款日大于等于支付日，且当前与支付日无差值
        //如果还款日小于支付日，且当前与支付日无差值
        //正常冲账

        //3.修改loanIou滞纳金
        if (deductionAmount.doubleValue() > 0) {
            loanIou.setFeeRemissionAmt(loanIou.getFeeRemissionAmt().add(deductionAmount));
            iLoanIouService.updateLoanIou(loanIou);
        }

        //4.查询有无repayemntOrder  Offline
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setActionType("Offline");
        repaymentOrder.setState("1");
        repaymentOrder.setRepayStatus(2);
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());
        List<RepaymentOrder> repaymentOrders = iRepaymentOrderService.selectRepaymentOrderList(repaymentOrder);

        if (repaymentOrders != null && repaymentOrders.size() > 0) {
            repaymentOrder = repaymentOrders.get(0);
            repaymentOrder.setReduceFee(deductionAmount);
            repaymentOrder.setShouldRepaymentAmt(repaymentOrder.getShouldRepaymentAmt().subtract(repaymentOrder.getReduceFee()));
            //修改减免金额
            repaymentOrder.setPayTime(payTime);
            repaymentOrderService.updateRepaymentOrder(repaymentOrder);
        } else {
            //新增repaymentOrder
            repaymentOrder = initRepaymentOrderToLoanIou(loanIou, "Offline", amount);
            repaymentOrder.setPayTransSerialNo("");
            repaymentOrder.setReduceFee(deductionAmount);
            repaymentOrder.setShouldRepaymentAmt(repaymentOrder.getShouldRepaymentAmt().subtract(repaymentOrder.getReduceFee()));
            repaymentOrder.setRemark(reqParam);
            repaymentOrder.setPayTime(payTime);
            repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        }

        //5.生成减免记录
        if (deductionAmount.doubleValue() > 0) {
            ReduceFee reduceFee = new ReduceFee();
            reduceFee.setReduceSerialNo(IdUtils.reqId());
            reduceFee.setLoanSerialNo(repaymentOrder.getLoanSerialNo());
            reduceFee.setRepaymentNo(repaymentOrder.getRepaymentNo());
            reduceFee.setCustNo(repaymentOrder.getCustNo());
            reduceFee.setTotalAmt(deductionAmount);
            reduceFee.setLateFee(deductionAmount);
            reduceFee.setReduceSource("submitOffline");
            String reduceDesc = "提交时间:" + DateUtils.dateTime(nowDate) + ",付款时间:" + DateUtils.dateTime(payTime) + ",应还本金:" + loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()) + ",逾期天数:" + overdueDay;
            reduceFee.setReduceDesc(reduceDesc);

            iReduceFeeService.insertReduceFee(reduceFee);

            LoanOffset offset = new LoanOffset();
            offset.setLoanSerialNo(repaymentOrder.getLoanSerialNo());
            offset.setRepaymentNo(repaymentOrder.getRepaymentNo());
            offset.setCustNo(repaymentOrder.getCustNo());
            offset.setAmt(deductionAmount);
            offset.setActionType("Reduce");
            offset.setLoanPrincipal(loanIou.getLoanPrincipal());
            offset.setRepaidPrincipal(loanIou.getRepaidPrincipal());
            offset.setServiceFee(loanIou.getServiceFee());
            offset.setRepaidServiceFee(loanIou.getRepaidServiceFee());
            offset.setLoanInterest(loanIou.getLoanInterest());
            offset.setRepaidInterest(loanIou.getRepaidInterest());
            offset.setLateFee(loanIou.getLateFee());
            offset.setRepaidLateFee(loanIou.getRepaidLateFee());
            offset.setRemissionNo(reduceFee.getReduceSerialNo());
            offset.setReduceAmt(deductionAmount);
            offset.setLoanOffsetStatus(1);
            offset.setLoanOffsetDesc("success");
            loanOffsetService.insertLoanOffset(offset);
        }

        //更新credit_account
        CreditAccount creditAccount = new CreditAccount();
        creditAccount.setCustNo(loanIou.getCustNo());
        creditAccount.setState("1");
        List<CreditAccount> accountList = creditAccountService.selectCreditAccountList(creditAccount);
        if (accountList.size() > 0) {
            //备份
            iBusinessService.backUpCreditAccount(accountList.get(0));
        }

        //6.冲账
        transactionStatusSuccess(loanIou, repaymentOrder, amount, payTime, "submitOffline");

        //查询新的LoanIou记录
        loanIou = iLoanIouService.selectLoanIouById(loanIou.getId());
        //7.生成新的滞纳金
        if (deductionAmount.doubleValue() > 0 && loanIou.getIouStatus() == 2 && backDays > 0) {
            //如果借据还处于逾期状态
            log.debug(loanIou.getLoanPrincipal() + "===" + loanIou.getRepaidPrincipal());
            //(本金-已还本金)*逾期天数*逾期费率=最新滞纳金
            BigDecimal multiply = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()).multiply(new BigDecimal(backDays)).multiply(loanIou.getDateLateFee());

            log.info("生成新的滞纳金{}", multiply);

            //更新滞纳金
            loanIou.setLateFee(loanIou.getLateFee().add(multiply));
            loanIou.setOverdueAmount(loanIou.getOverdueAmount().add(multiply));
            iLoanIouService.updateLoanIou(loanIou);

            if (accountList.size() > 0) {

                accountList.get(0).setLateFee(loanIou.getLateFee());
                accountList.get(0).setRepaidLateFee(loanIou.getRepaidLateFee());
                creditAccountService.updateCreditAccount(accountList.get(0));
            }

            //检查
            checkAmountLock(loanIou, "submitOffline");
        }


        return loanIou;
    }


    @Override
    public List<RepaymentOrder> getToActionTypeAndLoanSerialNo(String actionType, String loanSerialNo) {
        return repaymentMapper.getToActionTypeAndLoanSerialNo(actionType, loanSerialNo);
    }

    @Override
    public void closeByRepayemntNo(String paymentReference) {
        repaymentMapper.closeByRepayemntNo(paymentReference);
    }

    @Override
    public RepaymentOrder getMaxPayTime(String loanSerialNo) {
        return repaymentMapper.getMaxPayTime(loanSerialNo);
    }

}